/*
 * mkfs.vvsfs
 * The second edition of vvsfs file initial system.
 * Initial done by Eric McCreath, 2006 GPL
 * Construct by Tojo Saki and Mayazure Jerry.
 */

/* To compile :
 *   gcc mkfs.vvsfs.c -o mkfs.vvsfs
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <sys/types.h>
#include <unistd.h>
#include <termios.h>

#include "vvsfs.h"

#define DEFAULT_BLOCK_SIZE 1024

struct vvsfs_block *block=NULL;
struct vvsfs_device_block *device_block=NULL;
char* device_name;
int device;
int block_size=0;

static void die(char *mess) {
    fprintf(stderr,"Exit : %s\n",mess);
    exit(1);
}

static void usage(void) {
    printf("Usage : mkfs.vvsfs <device name> <parameter>\n"
           "The parameter should be the following:\n"
           "    -b <size>           Block size, it should be 512/1024/2048");
    exit(1);
}

static inline void write_block(off_t pos) {
    if (pos != lseek(device, pos, SEEK_SET)) {
        // move the file pointer to the correct block
        free(block);
        die("seek set failed");
    }
    if (block_size != write(device, block, block_size)){
        // write the block
        free(block);
        die("inode write failed");
    }
}

int main(int argc, char ** argv) {
    if (argc < 2) usage();
    //Set default block size.
    block_size=DEFAULT_BLOCK_SIZE;
    //Read parameter.
    int i;
    if(argc > 2) {
        for (i=2; i<argc; ++i) {
            if(strncmp(argv[i], "-b", 2)==0 ||
                    strncmp(argv[i], "--block", 7)==0) {
                ++i;
                block_size=atoi(argv[i]);
            } else {
                usage();
            }
        }
    }
    //Check block size.
    if(block_size!=512 && block_size!=1024 && block_size!=2048) {
        die("Invalid block size");
    }
    //Print header.
    printf(HEADER_IMAGES, "Raw File Formatter", VER_MAJOR, VER_MINOR, VER_PATCH, VER_SPECIAL);
    // open the device for reading and writing
    device_name = argv[1];
    printf("Device information\n"
           "Device       : %s\n", device_name);
    device = open(device_name, O_RDWR);

    //Get the device information.
    off_t device_size = lseek(device, 0, SEEK_END);
    printf("Device size  : %lld\n", (long long int)device_size);
    printf("Block size   : %d\n", block_size);
    //Calulate the block count.
    int block_count=device_size / block_size;
    printf("Block total  : %d\n", block_count);
    //Set to device block.
    off_t pos=0;
    //Generate cache.
    block=(struct vvsfs_block *)malloc(block_size);
    if(block==NULL) {
        die("no enough memory.");
    }
    //Clear the content.
    memset(block, 0, block_size);
    //Define the block structure.
    device_block=(struct vvsfs_device_block *)block;
    //Set the device block info.
    device_block->device_size=device_size;
    device_block->block_count=block_count;
    device_block->block_size=block_size;
    //Treat each block as one inode, double the size, calculate how many bytes we need for the inodes.
    //Calculate one block could store how many inodes.
    int block_max_inode=block_size / sizeof(struct vvsfs_inode);
    //Calculate how many block we need to store the inodes.
    int inode_block_count=((block_count << 1) - block_max_inode) / block_max_inode + 1;
    //Calculate root block id.
    int root_block_id=inode_block_count+BLOCK_INODE_LIST;
    device_block->inode_block_count=inode_block_count;
    device_block->last_used_block=root_block_id;
    printf("inode blocks : %d\n", inode_block_count);
    printf("Root block   : %d\n", root_block_id);
    //Write the content to raw data.
    write_block(pos);
    pos += block_size;
    //Write the journal state block info.
    pos += block_size;
    //Write journal content block info.
    pos += block_size;
    //Clear the content data.
    memset(block, 0, block_size);
    //Write the root inode data to the inode list.
    struct vvsfs_inode *root_inode=(struct vvsfs_inode *)block;
    //Write root inode information.
    root_inode->block=root_block_id;
    root_inode->count=1;
    root_inode->flag = 0;
    root_inode->flag |= VVSFS_IFLAG_ISUSED;
    root_inode->size = 0;
    root_inode->c_size=0;
    //Write the block.
    write_block(pos);
    pos += block_size;
    //Clear the content data.
    memset(block, 0, block_size);
    //Write all the left block size data.
    for(i=BLOCK_INODE_LIST+1; i<root_block_id; ++i) {
        //Write the empty block data.
        write_block(pos);
        pos += block_size;
    }
    printf("writing      : 0x00000000");
    block->hash=0;
    block->e_size=0;
    //Write the content data from root to the end of the file.
    for (i = root_block_id; i < block_count; i++) {  // write each of the blocks
        printf("\b\b\b\b\b\b\b\b%08x",i);
        block->flag=0;
        //Mark all the blocks which is not the root dir block to be empty.
        if (i == root_block_id) {
            block->count=1;
        } else {
            block->count=0;
            block->flag |= VVSFS_FLAG_ISEMPTY;
        }
        write_block(pos);
        pos += block_size;
    }
    printf("\n");
    //Free the block, close the device.
    free(block);
    close(device);
    return 0;
}

